package com.project.service.impl;


import com.base.entity.bo.SciencePersonBo;
import com.base.entity.vo.SciencePersonVo;
import com.base.mapper.SciencePersonMapper;
import com.common.api.ApprovalApi;
import com.common.msg.service.SendMessageService;
import com.common.utils.DateUtils;
import com.common.utils.SecurityUtils;
import com.common.utils.uuid.IdUtils;
import com.feemanage.service.ISysSnruleService;
import com.flowable.domain.instance.BpmProcessInstanceCancelReqVO;
import com.flowable.domain.vo.task.BpmTaskApproveReqVO;
import com.flowable.service.IFlowInstanceService;
import com.flowable.service.IFlowTaskService;
import com.framework.handler.MyMetaObjectHandler;
import com.project.comom.NumberDealUtils;
import com.project.controller.ProjectApplyController;
import com.project.domain.bo.ProjectBudgetBo;
import com.project.domain.dto.projectApplyDto.*;
import com.project.domain.dto.projectCheckDto.SearchExcelReqDto;
import com.project.domain.dto.projectReviewDto.LixiangReqDto;
import com.project.domain.entity.*;
import com.project.domain.entity.xjProject.BizXjProject;
import com.project.domain.entity.xjProject.BizXjProjectMember;
import com.project.domain.vo.BizProjectAppBookBudgetFeeSaveVo;
import com.project.domain.vo.BizProjectApplyBookVo;
import com.project.domain.vo.BizProjectApplyInfoVo;
import com.project.mapper.ProjectApplyMapper;
import com.project.service.IProjectApplyService;
import com.project.service.IProjectCheckService;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;

import static com.project.comom.DateDealUtils.castInputDateStrToRequireFormat;

/**
 * Created by cw13031 on 2025/2/6.
 * Description:
 */
@Service
public class ProjectApplyServiceImpl implements IProjectApplyService {

    @Autowired
    ProjectApplyMapper projectApplyMapper;

    @Autowired
    IProjectCheckService projectCheckService;


    @Autowired
    private MyMetaObjectHandler autoFillFieldsHandler;

    @Autowired
    private BudgetStandardServiceImpl budgetStandardService;

    @Autowired
    SciencePersonMapper sciencePersonMapper;


    @Autowired
    ProjectInitServiceImpl projectInitService;

    @Autowired
    ProjectBudgetServiceImpl budgetService;

    @Autowired
    ProjectMemberServiceImpl memberService;

    @Autowired
    private ISysSnruleService sysSnruleService;

    @Autowired
    private SendMessageService sendMessageService;

    @Autowired
    private ApprovalApi approvalApi;

    @Autowired
    private IFlowInstanceService flowInstanceService;

    private final static String module_name = "申报项目";


    private static final String CODE_YITIJIAO = "0";

    @Autowired
    private IFlowTaskService flowTaskService;

    @Override
    public List<Object> test() {
        return projectApplyMapper.test();
    }

    @Override
    public List<HashMap<String,Object>> list(BizProjectApplyInfoVo bizProjectApplyInfoVo) {
        String userId = SecurityUtils.getUserId().toString();
        bizProjectApplyInfoVo.setCreateUserid(userId);
        //处理日期格式
        changeInputDateFormat(bizProjectApplyInfoVo);
        return projectApplyMapper.list(bizProjectApplyInfoVo);
    }

    @Override
    public List<HashMap<String, Integer>> listStatistics(BizProjectApplyInfoVo bizProjectApplyInfoVo) {
        //处理日期格式
        changeInputDateFormat(bizProjectApplyInfoVo);
        return projectApplyMapper.listStatistics(bizProjectApplyInfoVo);
    }

    @Override
    public void add(BizProjectApplyInfo bizProjectApplyInfo) {
        MetaObject metaObject = MyMetaObjectHandler.getMetaObject(bizProjectApplyInfo);
        autoFillFieldsHandler.fillForInsert(metaObject);
        projectApplyMapper.add(bizProjectApplyInfo);
    }

    @Override
    public ApplyInfoViewResDto getApplyInfoById(String id) {
        ApplyInfoViewResDto res = projectApplyMapper.getApplyInfoById(id);
        if (res == null) {
            throw new IllegalArgumentException("未找到对应的申请信息【info】");
        }
        return res;
    }

    @Override
    public ArrayList<HashMap<String, String>> getFileInfoByFileIds(String[] fileIdArray) {
        return projectApplyMapper.getFileInfoByFileIds(fileIdArray);
    }

    @Override
    public void edit(BizProjectApplyInfo bizProjectApplyInfo) {
        MetaObject metaObject = MyMetaObjectHandler.getMetaObject(bizProjectApplyInfo);
        autoFillFieldsHandler.fillForUpdate(metaObject);
        projectApplyMapper.edit(bizProjectApplyInfo);
    }

    @Override
    public void delete(List<String> ids) {
        projectApplyMapper.delete(ids);
    }

    @Override
    public void changeState(BizProjectApplyInfo bizProjectApplyInfo) {
        makeAlert(bizProjectApplyInfo);
        projectApplyMapper.changeState(bizProjectApplyInfo);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void materialsAdd(BizProjectApplyBookVo bizProjectApplyBookVo) {
        MetaObject metaObject = MyMetaObjectHandler.getMetaObject(bizProjectApplyBookVo);
        autoFillFieldsHandler.fillForInsert(metaObject);

        String uuid = IdUtils.simpleUUID();
        bizProjectApplyBookVo.setId(uuid);

        bizProjectApplyBookVo.setApplyDate(LocalDate.now());

        projectApplyMapper.materialsAdd_1_book(bizProjectApplyBookVo);
        projectApplyMapper.materialsAdd_2_member(bizProjectApplyBookVo);
    }

    @Override
    public HashMap<String,Object> materialsGetInfo(BizProjectApplyBookVo bizProjectApplyBookVo) {
        String userId = SecurityUtils.getUserId().toString();
        bizProjectApplyBookVo.setCreateUserid(userId);
        return projectApplyMapper.materialsGetInfo(bizProjectApplyBookVo);

    }

    @Override
    public HashMap<String,Object> getBookByBooId(BizProjectApplyBookVo bizProjectApplyBookVo){
        HashMap<String, Object> res = projectApplyMapper.getBookByBookId(bizProjectApplyBookVo);
        addSomeFields(res);

        //region 元-->万元
        NumberDealUtils.mapYuan2Wan(res,"applyFee");
        //endregion

        return res;
    }

    @Override
    public String materialsSaveInfo(BizProjectApplyBookVo bizProjectApplyBookVo) {
        //region 万元-->元。
        String feeYuan = NumberDealUtils.multiply_10000(bizProjectApplyBookVo.getApplyFee());
        bizProjectApplyBookVo.setApplyFee(feeYuan);
        //endregion

        if (!StringUtils.hasText(bizProjectApplyBookVo.getId())){
            // 数据 插入时 自动记录的字段
            MetaObject metaObject = MyMetaObjectHandler.getMetaObject(bizProjectApplyBookVo);
            autoFillFieldsHandler.fillForInsert(metaObject);
            //生成主键id
            String uuid = IdUtils.simpleUUID();
            bizProjectApplyBookVo.setId(uuid);
            //生成”申请日期“字段
            bizProjectApplyBookVo.setApplyDate(LocalDate.now());
            projectApplyMapper.materialsSaveInfoInsert(bizProjectApplyBookVo);
            // 业务设计如此：第一次插入时，会附带插入本人为第一个成员
            insertOneMemberWhenBookInit(bizProjectApplyBookVo);
        }else {
            // 数据 更新时 自动记录的字段
            MetaObject metaObject = MyMetaObjectHandler.getMetaObject(bizProjectApplyBookVo);
            autoFillFieldsHandler.fillForUpdate(metaObject);
            projectApplyMapper.materialsSaveInfoUpdate(bizProjectApplyBookVo);
        }
        adaptSubmitType(bizProjectApplyBookVo);
        return bizProjectApplyBookVo.getId();
    }

    @Override
    public List<HashMap<String, Object>> materialsGetMember(BizProjectApplyBookVo bizProjectApplyBookVo) {
        return projectApplyMapper.materialsGetMember(bizProjectApplyBookVo);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void materialsSaveMember(BizProjectApplyBookVo bizProjectApplyBookVo) {
        projectApplyMapper.materialsDeleteMember(bizProjectApplyBookVo);
        projectApplyMapper.materialsSaveMember(bizProjectApplyBookVo);
        adaptSubmitType(bizProjectApplyBookVo);
    }

    @Override
    public BizProjectApplyBookVo getApplyBookByBookId(BizProjectApplyBookVo bizProjectApplyBookVo){
        BizProjectApplyBookVo book = projectApplyMapper.getApplyBookByBookId(bizProjectApplyBookVo);
        //region 元-->万元
        String feeYuan = NumberDealUtils.divide_10000(book.getApplyFee());
        book.setApplyFee(feeYuan);

        //endregion
        return book;
    }

    @Override
    public List<HashMap<String,Object>> getBudget(String projectTypeCode, String projectStartDate, String applyBookId) {
        List<HashMap<String,Object>> res = projectApplyMapper.getBudgetByArgs(projectTypeCode,projectStartDate,applyBookId);
        List<HashMap<String, Object>> hashMaps = buildTree(res);
//        budgetStandardService.buildTree()
        return hashMaps;
    }

    @Override
    public List<HashMap<String,Object>> getBudgetV2(String applyBookId) {
        List<HashMap<String,Object>> res = projectApplyMapper.getBudgetByArgsV2(applyBookId);
        //region 元-->万元
        NumberDealUtils.mapListYuan2Wan(res,"subjectFee");
        //endregion
        return buildTree(res);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveBudget(BizProjectAppBookBudgetFeeSaveVo bizProjectAppBookBudgetFeeSaveVo) {

        ArrayList<BizProjectAppBookBudgetFee> withIdBudgetsList = new ArrayList<>();
        ArrayList<BizProjectAppBookBudgetFee> withoutIdBudgetsList = new ArrayList<>();

        List<BizProjectAppBookBudgetFee> budgets = bizProjectAppBookBudgetFeeSaveVo.getBudgets();
        for (BizProjectAppBookBudgetFee b :budgets) {
            //region 万元-->元。
            String feeYuan = NumberDealUtils.multiply_10000(b.getSubjectFee());
            b.setSubjectFee(feeYuan);
            //endregion
            b.setApplyBookId(bizProjectAppBookBudgetFeeSaveVo.getId());
            if (StringUtils.hasText(b.getId())){
                withIdBudgetsList.add(b);
            }else {
                withoutIdBudgetsList.add(b);
            }
        }

        if (!withIdBudgetsList.isEmpty()){
            // 编辑状态：获取所有预算id. 先删除后插入
            ArrayList<String> budgetIdList = new ArrayList<>();
            for (BizProjectAppBookBudgetFee o :withIdBudgetsList) {
                budgetIdList.add(o.getId());
            }
            int i = projectApplyMapper.deleteBudgetsByBudgetId(budgetIdList);
            projectApplyMapper.saveBudget(withIdBudgetsList);
//            projectApplyMapper.updateBudget(withIdBudgetsList);
        }

        if (!withoutIdBudgetsList.isEmpty()){
            // 新建状态
            projectApplyMapper.saveBudget(withoutIdBudgetsList);
        }

        // 为了适配adaptSubmitType方法，构造其入参BizProjectApplyBookVo对象
        BizProjectApplyBookVo tmp = new BizProjectApplyBookVo();
        tmp.setId(bizProjectAppBookBudgetFeeSaveVo.getId());
        tmp.setSubmitType(bizProjectAppBookBudgetFeeSaveVo.getSubmitType());
        adaptSubmitType(tmp);
    }

    public static List<HashMap<String, Object>> buildTree(List<HashMap<String, Object>> flatList) {
        // 使用Map存储所有节点，便于查找
        Map<String, HashMap<String, Object>> nodeMap = new HashMap<>();
        // 存储顶级节点
        List<HashMap<String, Object>> roots = new ArrayList<>();

        // 第一步：把所有节点放入Map，并初始化children列表
        for (HashMap<String, Object> node : flatList) {
            String subjectCode = (String) node.get("subjectCode");
            node.put("children", new ArrayList<HashMap<String, Object>>()); // 添加children属性
            nodeMap.put(subjectCode, node);
        }

        // 第二步：构建树形结构
        for (HashMap<String, Object> node : flatList) {
            String upCode = (String) node.get("upCode");

            // 如果upCode为空或不存在，则是根节点
            if (upCode == null || upCode.isEmpty()) {
                roots.add(node);
            } else {
                // 找到父节点并添加到其children中
                HashMap<String, Object> parent = nodeMap.get(upCode);
                if (parent != null) {
                    List<HashMap<String, Object>> children =
                            (List<HashMap<String, Object>>) parent.get("children");
                    children.add(node);
                }
            }
        }

        return roots;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    @Deprecated
    public int changeBookState(ChangeBookStateDto dto) {
        int totalRows = 0;

        String[] bookIds = dto.getId().split(",");
        for (String bookId : bookIds) {
            if (bookId.trim().isEmpty()) {
                continue; // 跳过空 ID
            }

            dto.setId(bookId);
            // 修改book表状态
            int rows = projectApplyMapper.changeBookState(dto);
            totalRows += rows;
            // 写入log① 构造日志表字段
            BizCheckLog bizCheckLog = buildBizCheckLogFields(dto);
            // 写入log② 数据写入日志表
            projectCheckService.changeCheckState(bizCheckLog);
        }
        return totalRows; // 返回总受影响行数
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteBook(List<String> ids) {
        //region 删除流程实例
        for (String id : ids) {
            delWorkFlowInstance(id);
        }
        //endregion
        int x = projectApplyMapper.deleteApplyBookByBookId(ids);
        int y = projectApplyMapper.deleteApplyBookMemberByBookId(ids);
        int z = projectApplyMapper.deleteApplyBookBudgetByBookId(ids);

        //region fix 在删除book时，为了清除冗余数据，还需删除专家评审的相关数据
        // 这部分代码似乎永远不会实际生效，因为不评审的不会有评审数据，而要评审的book在存在评审数据时，页面已经不允许删除
//        List<String> expertReviewIds = projectApplyMapper.getExpertReviewIdsByBookId(ids);
//        int A = projectApplyMapper.deleteExpertReviewByBookId(ids);
//        if (expertReviewIds != null && !expertReviewIds.isEmpty()){
//            int B = projectApplyMapper.deleteReviewResultByExpertReviewId(expertReviewIds);
//        }
        //endregion
    }

    /**
     * 构造审核流程节点对象
     * @param dto x
     * @return x
     * @deprecated 该方法已被废弃，由于接入了流程引擎，审核日志由该模块自动处理，因此该接口不再会被调用。
     * 但是，其内部updateProjectId(dto,book)方法应该在外部重新调用
     */
    @Deprecated
    public BizCheckLog buildBizCheckLogFields(ChangeBookStateDto dto){
        // 通过bookId查询book，用以获得bookName
        BizProjectApplyBookVo vo = new BizProjectApplyBookVo();
        vo.setId(dto.getId());
        BizProjectApplyBookVo book = getApplyBookByBookId(vo);
        //
        BizCheckLog bizCheckLog = new BizCheckLog();
        bizCheckLog.setBizObjId(book.getId());
        bizCheckLog.setBizObjName(book.getName());
        bizCheckLog.setCheckStatus(dto.getStateCode());
        bizCheckLog.setCheckInfo(dto.getMsg());

        MetaObject metaObject = MyMetaObjectHandler.getMetaObject(bizCheckLog);
        autoFillFieldsHandler.fillForCheckX(metaObject);
        // 更新projectId（updateProjectId方法在这里调用是为了利用现成的book对象）
        updateProjectId(dto,book);
        return bizCheckLog;
    }

    /**
     * 当第一次提交时，会生成projectId，
     * 因此当dto.stateCode = 0时，判断当前数据是否有projectId,
     * 如果没有，就调用hyq接口，
     * 如果有， 就不处理
     * @return int 更新的行数
     */
    public int updateProjectId(ChangeBookStateDto dto,BizProjectApplyBookVo book){
        // 只有在提交时才会触发
        if (!CODE_YITIJIAO.equals(dto.getStateCode())) {
            return 0;
        }
        // 第一次提交
        if (!StringUtils.hasText(book.getProjectId())){
//            String projectId = IdUtils.simpleUUID();
            String projectId = sysSnruleService.generateCode("2",book.getId());
            return projectCheckService.updateProjectId(dto.getId(),projectId);
        }
        return 0;
    }

    /**
     * 第一次暂存book基本信息时，除了保存book表还会默认添加一个人员到member表
     * @param bizProjectApplyBookVo
     * @return
     */
    public int insertOneMemberWhenBookInit(BizProjectApplyBookVo bizProjectApplyBookVo){
        // 通过applier获取用户信息
        SciencePersonVo sciencePersonVo = new SciencePersonVo();
        sciencePersonVo.setId(bizProjectApplyBookVo.getApplierId());
        List<SciencePersonBo> sciencePersonBos = sciencePersonMapper.selectPersonDetails(sciencePersonVo);
        // 获取用户成功
        if (!sciencePersonBos.isEmpty()){
            SciencePersonBo sciencePersonBo = sciencePersonBos.get(0);

            BizProjectApplyBookMember initMember = new BizProjectApplyBookMember();
            initMember.setMemberType("1");
            initMember.setPersonId(sciencePersonBo.getId());
            initMember.setPersonName(sciencePersonBo.getName());
            initMember.setSexId(sciencePersonBo.getSexId());
            initMember.setUnitId(sciencePersonBo.getUnitId());
            initMember.setUnitName(sciencePersonBo.getUnitName());

         //   Date sqlDate = new java.sql.Date(sciencePersonBo.getBirthday().getTime());
       //    LocalDate localDate = sqlDate.toLocalDate();
//            initMember.setBirthday(LocalDateTime.of(sciencePersonBo.getBirthday(), LocalTime.MIN));
            //申请人信息都有了，不用单独再取一次人员信息
            initMember.setBirthday(bizProjectApplyBookVo.getBirthday());

            initMember.setExecutivePost(bizProjectApplyBookVo.getExecutivePost());
            initMember.setTitleId(sciencePersonBo.getTitleId());
            initMember.setDivision("");
            initMember.setOrderId(1);

            ArrayList<BizProjectApplyBookMember> memberLi = new ArrayList<>();
            memberLi.add(initMember);
            bizProjectApplyBookVo.setMembers(memberLi);
            materialsSaveMember(bizProjectApplyBookVo);
            return 1;
        }
        // 获取用户失败
        else{
            return 0;
        }
    }

    /**
     * 立项逻辑：
     * biz_project_apply_book ①修改立项状态book.PROJECT_STATE ②写入xjid ③插入xj主表 ④写入xj人员 ⑤写入buget)batch ⑥写入buget_fee
     * 取消立项逻辑：恢复上述操作
     * @param dto
     * @return
     */
    @Override
    public int lixiang(LixiangReqDto dto) {

        BizProjectApplyBook book = getBizBookByBookId(dto.getBookId());
        if ("2".equals(dto.getProjectState())){
            book = updateBookWhileLixiang(dto);
            // 复制主表
            int i = copyFromBookToXjProject(book,null);

            // 复制成员信息到xj表，返回原始book成员表
            ArrayList<BizProjectApplyBookMember> members = copyMember(book);
            
            // 复制预算信息到fee_batch和budget_fee表
            int i1 = copyBudget(book);
        }else if ("0".equals(dto.getProjectState())){
            //region 如果处于“申请中”，则不可撤销
            boolean allow = allowLixiangRollBack(book.getProjectId());
            if (!allow){
                throw new RuntimeException("改项目处于申请中状态，无法取消立项");
            }
            //endregion
            book = updateBookWhileLixiang(dto);
            projectInitService.delXjInfos(new String[] {book.getProjectId()});
        }else {
            throw new RuntimeException("错误的ProjectState："+dto.getProjectState());
        }
        return 0;
    }

    /**
     * 通过porjectId在校级表中的状态，返回当前项目是否在 申请中 状态，
     * 如果是申请中，则不允许取消立项
     * @param projectId x
     * @return x
     */
    private boolean allowLixiangRollBack(String projectId) {
        String checkStatus = projectApplyMapper.allowLixiangRollBack(projectId);
        return !MyConstants.CHECKING.equals(checkStatus);
    }

    @Override
    public ArrayList<ApplyHistoryResDto> applyHistory(ApplyHistoryReqDto dto) {
        changeInputDateFormat(dto);
        changeInputFeeFormat(dto);
        ArrayList<ApplyHistoryResDto> list = projectApplyMapper.applyHistory(dto);

        //region 元-->万元
        for (ApplyHistoryResDto t : list) {
            t.setApplyFee(NumberDealUtils.divide_bigDecimal(t.getApplyFee()));
        }
        //endregion

        return list;
    }

    @Override
    public ArrayList<IJoinResDto> i_join(IJoinReqDto dto) {
        ArrayList<IJoinResDto> proList = projectApplyMapper.i_join(dto);
        //region 元-->万元
        proList.forEach(t -> {
            t.setFee(NumberDealUtils.divide_bigDecimal(t.getFee()));
        });
        //endregion
        return proList;
    }

    @Override
    public ArrayList<ExportHistoryApplyBookMybatisDto> exportHistoryApplyExcel(ApplyHistoryExcelReqDto dto) {
        changeInputDateFormat(dto);
        return projectApplyMapper.exportHistoryApplyExcel(dto);
    }

    @Override
    public ArrayList<ExportApplyBookMybatisDto> exportBookExcel(SearchExcelReqDto dto) {
        dto.setApplyDate1(castInputDateStrToRequireFormat(dto.getApplyDate1()));
        dto.setApplyDate2(castInputDateStrToRequireFormat(dto.getApplyDate2()));
        return projectApplyMapper.exportBookExcel(dto);
    }

    private BizProjectApplyBook getBizBookByBookId(String bookId){
        BizProjectApplyBook book = projectApplyMapper.getBizBookByBookId(bookId);
        return book;
    }

    private BizProjectApplyBook updateBookWhileLixiang(LixiangReqDto dto){
        String projectState = dto.getProjectState();
        String bookId = dto.getBookId();

        BizProjectApplyBook book = getBizBookByBookId(bookId);

        if (book==null){
            throw new RuntimeException("此bookId错误");
        }

        //进行立项
        if ("2".equals(projectState)){
            book.setProjectId(IdUtils.simpleUUID());
            book.setProjectState(projectState);
            projectApplyMapper.updateBookWhileLixiang(book);
            return book;
        //取消立项
        }else if ("0".equals(projectState)){
            String projectId = book.getProjectId();

            book.setProjectId(null);
            book.setProjectState(projectState);
            projectApplyMapper.updateBookWhileLixiang(book);
            book.setProjectId(projectId);
            return book;
        }else {
            throw new RuntimeException("错误的ProjectState："+projectState);
        }
    }

    /**
     * 复制book主表信息到xjproject表
     * @return x
     */
    private int copyFromBookToXjProject(BizProjectApplyBook book,ArrayList<BizProjectApplyBookMember> members){
        BizXjProject bizXjProject = new BizXjProject();
        bizXjProject.setId(book.getProjectId());
        bizXjProject.setName(book.getName());
        bizXjProject.setUnitId(book.getUnitId());
        bizXjProject.setProjectLevelId("5");//校级项目固定值 5

        Date now = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd 00:00:00.000");
        bizXjProject.setAuthorizeDate(sdf.format(now));
        bizXjProject.setBeginDate(book.getPlanStartDate().toString());
        bizXjProject.setPlanEndDate(book.getPlanEndDate().toString());
        bizXjProject.setProjectStatusId("0");
        // 负责人
        bizXjProject.setChargerType("1");//教师
        bizXjProject.setChargerCode(book.getApplierId());
        bizXjProject.setChargerName(book.getApplierName());
//        bizXjProject.setChargerPhone("");//原始页面就没有写入
//        bizXjProject.setChargerEmail("");//原始页面就没有写入
        bizXjProject.setProjectTypeCode(book.getProjectTypeId());
        bizXjProject.setFeeAuthorize(book.getApplyFee());
        bizXjProject.setLevelType(null);
        bizXjProject.setNote(book.getNote());
        bizXjProject.setCheckstatus("-2");

        bizXjProject.setResearchbasetype(book.getResearchBaseType());
        bizXjProject.setResearchbase(book.getResearchBase());
        bizXjProject.setTechnologyactivitytype(book.getTechnologyActivityType());
        bizXjProject.setNationalindustrycode(book.getNationalIndustryCode());
        bizXjProject.setBirthday(book.getBirthday()!=null?book.getBirthday().toString():null);
        bizXjProject.setKeyword(book.getKeyword());

        bizXjProject.setCompletedatastatus("notComplete");
        String standardId = getBudgetStandardIdByInfoId(book.getApplyInfoId());
        bizXjProject.setBudgetStandardId(standardId);

        projectInitService.saveXjProInfos(bizXjProject);
        return 0;
    }

    /**
     * 复制成员信息到xj表，返回原始book成员表
     * 从BIZ_PROJECT_APPLY_BOOK_MEMBER复制成员信息到BIZ_XJ_PROJECT_MEMBER
     * @return 返回BIZ_PROJECT_APPLY_BOOK_MEMBER，主表构建时需要用到
     */
    private ArrayList<BizProjectApplyBookMember> copyMember(BizProjectApplyBook book){
        ArrayList<BizProjectApplyBookMember> bizProjectApplyBookMembers = projectApplyMapper.gtMembersByBookId(book.getId());

        ArrayList<BizXjProjectMember> bizXjProjectMembers = new ArrayList<>();
        for (BizProjectApplyBookMember bookMember :bizProjectApplyBookMembers) {
            BizXjProjectMember xjMember = new BizXjProjectMember();
            xjMember.setProjectId(book.getProjectId());
            xjMember.setMemberType("1");
            xjMember.setPersonId(bookMember.getPersonId());
            xjMember.setPersonCode("账号缺失");//todo 账号 在book表中没有保存该值
            xjMember.setPersonName(bookMember.getPersonName());
            xjMember.setUnitId(bookMember.getUnitId());
            xjMember.setUnitName(bookMember.getUnitName());
            xjMember.setSubjectId(bookMember.getSubjectId());
            xjMember.setTitleId(bookMember.getTitleId());
            xjMember.setEduDegreeId(bookMember.getEduDegree());
            xjMember.setSexId(bookMember.getSexId());
            xjMember.setOrderId(Long.valueOf(bookMember.getOrderId()));
            //region 当成员的orderId为1时,承担类型为负责人（1）
            if (xjMember.getOrderId()==1){
                xjMember.setBearTypeId("1");
            }else {
                xjMember.setBearTypeId(null);
            }
            //endregion

            bizXjProjectMembers.add(xjMember);
        }
        String s = memberService.saveXjMemberInfos(bizXjProjectMembers);
        return bizProjectApplyBookMembers;
    }

    private int copyBudget(BizProjectApplyBook book){
        ProjectBudgetBo projectBudgetBo = new ProjectBudgetBo();


        BizProjectBudgetBatch batch = new BizProjectBudgetBatch();
        batch.setProjectId(book.getProjectId());
        batch.setBudgetDate(new Date());
        batch.setBudgetFee(book.getApplyFee());
        batch.setCheckstatus("2");
        batch.setProjectClass("xJProject");
        batch.setBudgetStandardId(getBudgetStandardIdByInfoId(book.getApplyInfoId()));

        ArrayList<BizProjectBudgetFee> feeList = new ArrayList<>();
        List<BizProjectAppBookBudgetFee> bookFeeList = projectApplyMapper.getBudgetByBookId(book.getId());
        for (BizProjectAppBookBudgetFee bookFee : bookFeeList) {
            BizProjectBudgetFee xjFee = new BizProjectBudgetFee();
            xjFee.setBudgetStandardId(getBudgetStandardIdByInfoId(book.getApplyInfoId()));
            xjFee.setProjectId(book.getProjectId());
            xjFee.setSubjectCode(bookFee.getSubjectCode());
            xjFee.setSubjectFee(bookFee.getSubjectFee());
            xjFee.setBudgetType("liuxiao");
            xjFee.setSubjectId(bookFee.getSubjectId());
            xjFee.setFeeRatio(new BigDecimal(bookFee.getFeeRatio()));
            feeList.add(xjFee);
        }

        projectBudgetBo.setBizProjectBudgetBatch(batch);
        projectBudgetBo.setBizProjectBudgetFee(feeList);

//        projectInitService.saveBudget(projectBudgetBo);
        budgetService.saveBudgetInfos(projectBudgetBo,"apply");
        return 1;
    }

    /**
     * 通过infoId获取其预算标准id
     * @param infoId infoId
     * @return 预算标准id
     */
    private String getBudgetStandardIdByInfoId(String infoId){
        return projectApplyMapper.getBudgetStandardIdByInfoId(infoId);
    }

    /**
     * todo 需要前端lyz传递额外参数：title和endDate
     * 在info启动时，对外广播
     * @param bizProjectApplyInfo x
     */
    private void makeAlert(BizProjectApplyInfo bizProjectApplyInfo){
        //申请状态：0未启动，1申报中，2已停止
        String newApplyState = bizProjectApplyInfo.getApplyState();
        // 点击启动
        if ("1".equals(newApplyState)){
            String title = bizProjectApplyInfo.getName();
            String endDate = bizProjectApplyInfo.getEndDate();
            sendMessageService.batchSendMessageDeclare(title,endDate);
        }
    }


    /**
     * 适配接口新增参数submitType,因为要在接口处理暂存和提交
     * 该方法是为了适配流程模块
     */
    private void adaptSubmitType(BizProjectApplyBookVo book){
        String submitType = book.getSubmitType();
        if ("-1".equals(submitType)){//①初始时，科研人员点击【下一步】 ②科研人员二次编辑时，点击【暂存】 ③科研秘书/管理员点击【保存】
            //不进行流程相关操作
        }else if ("1".equals(submitType)){//①科研人员点击【提交】，会触发流程
            initProcess(book.getId());
            //适配projectCode字段
            updateProjectCode(book);
        }else if ("2".equals(submitType)) {//①科研人员/管理员 点击【保存和通过】
            BizProjectApplyBookVo tmpBook = getApplyBookByBookId(book);
            String checkStatus = tmpBook.getCheckStatus();
            // 审核状态=审核中：说明流程已经启动，后续调用接口完成流程
            if (MyConstants.CHECKING.equals(checkStatus)){
                // 通过流程id获取taskId
                Map<String, String> instanceTaskMap = approvalApi.getInstanceTaskMap(module_name);
                String taskId = instanceTaskMap.get(tmpBook.getProcessInstanceId());
                // 用taskId推进流程
                BpmTaskApproveReqVO bta = new BpmTaskApproveReqVO();
                bta.setId(taskId);
                bta.setReason(SecurityUtils.getNickName() + "保存并通过");
                flowTaskService.approveTask(SecurityUtils.getUserId(), bta);
            }else {
                //审核状态=其他：主要是暂存。暂存未提交也可以完成审核。
                // 手动修改checkState = 2 表示通过，手动修改流程id = 0
                book.setCheckStatus(MyConstants.CHECK_PASSED);
                book.setProcessInstanceId("0");
                book.setChecker(SecurityUtils.getNickName());
                book.setCheckDate(DateUtils.getTime());
                projectApplyMapper.adaptSubmitType(book);
            }
        }else {
            throw new RuntimeException("错误的submitType值："+submitType);
        }
    }

    /**
     * 发起一个流程
     * @param bookId
     * @return
     */
    private String initProcess(String bookId){
        return approvalApi.businessEntryApproval(bookId, module_name);
    }


    @Override
    public int bookSubmit(BookSubmitDto dto) {
        int totalRows = 0;
        String[] bookIds = dto.getId().split(",");
        for (String bookId : bookIds) {
            if (bookId.trim().isEmpty()) {
                continue; // 跳过空 ID
            }
            totalRows+=1;
            initProcess(bookId);

            //region 一鼓作气方式提交时，也需要适配projectCode字段
            BizProjectApplyBookVo book = new BizProjectApplyBookVo();
            book.setId(bookId);
            updateProjectCode(book);
            //endregion
        }
        return totalRows; // 返回总受影响行数
    }

    /**
     * 在首页场景下，该接口{@link ProjectApplyController#getInfo(BizProjectApplyBookVo)}需要额外参数taskId isAuditor checkState checkDate checker
     * 其中 checkState checkDate checker 在原接口查询
     */
    void addSomeFields(HashMap<String, Object> obj){
        //获取当前用户所有的代办列表
        Map<String, String> instanceTaskMap = approvalApi.getInstanceTaskMap(MyConstants.MODULE_NAME);

        String processInstanceId = obj.get("processInstanceId") != null
                ? obj.get("processInstanceId").toString()
                : "";
        // 判断是否是审核人，并设置taskId
        if (com.common.utils.StringUtils.isNotEmpty(processInstanceId) && instanceTaskMap.containsKey(processInstanceId)) {
            obj.put("taskId",instanceTaskMap.get(processInstanceId));
            obj.put("isAuditor",true);
        } else {
            obj.put("taskId","");
            obj.put("isAuditor",false);
        }
    }

    /**
     * 适配{@link ProjectApplyServiceImpl#adaptSubmitType(BizProjectApplyBookVo)}
     * 当用户提交时，需要生成projectCode并保存，虽然这个字段没有用，当时前端要展示
     * @param book
     */
    int updateProjectCode(BizProjectApplyBookVo book){
        String ProjectCode = sysSnruleService.generateCode("3","x");
        book.setProjectCode(ProjectCode);
        return projectApplyMapper.updateProjectCode(book);
    }


    void changeInputDateFormat(BizProjectApplyInfoVo bizProjectApplyInfoVo) {
        bizProjectApplyInfoVo.setBeginDate1(castInputDateStrToRequireFormat(bizProjectApplyInfoVo.getBeginDate1()));
        bizProjectApplyInfoVo.setBeginDate2(castInputDateStrToRequireFormat(bizProjectApplyInfoVo.getBeginDate2()));
        bizProjectApplyInfoVo.setEndDate1(castInputDateStrToRequireFormat(bizProjectApplyInfoVo.getEndDate1()));
        bizProjectApplyInfoVo.setEndDate2(castInputDateStrToRequireFormat(bizProjectApplyInfoVo.getEndDate2()));
    }

    void changeInputDateFormat(ApplyHistoryReqDto dto) {
        dto.setApplyDate1(castInputDateStrToRequireFormat(dto.getApplyDate1()));
        dto.setApplyDate2(castInputDateStrToRequireFormat(dto.getApplyDate2()));
    }

    void changeInputFeeFormat(ApplyHistoryReqDto dto) {
        if (dto.getMinFee()!=null){
            dto.setMinFee(NumberDealUtils.multiply_10000(dto.getMinFee()));
        }

        if (dto.getMaxFee()!=null){
            dto.setMaxFee(NumberDealUtils.multiply_10000(dto.getMaxFee()));
        }
    }

    /**
     * 撤销（删除）book时，如果没有流程实例，则不需要删除；如果有流程实例，则需要删除
     * @param bookId
     */
    void delWorkFlowInstance(String bookId) {
        BizProjectApplyBookVo book = new BizProjectApplyBookVo();
        book.setId(bookId);
        book = getApplyBookByBookId(book);

        if (MyConstants.SAVED.equals(book.getCheckStatus())){
            //表示没有提交过。即：用户自行撤销，没有流程实例相关操作需要处理
        }
        else if (MyConstants.CHECKING.equals(book.getCheckStatus())){
            //表示已经提交过，但还处于审核阶段，后续审核人员未进行审核。即：用户自行撤销，需要删除流程实例
            BpmProcessInstanceCancelReqVO cancelReqVO = new BpmProcessInstanceCancelReqVO();
            cancelReqVO.setId(book.getProcessInstanceId());
            cancelReqVO.setReason("主动删除");
            flowInstanceService.cancelProcessInstanceByStartUserForce(SecurityUtils.getLoginUser().getUserId(),cancelReqVO);
        }
        else if (MyConstants.CHECK_PASSED.equals(book.getCheckStatus())){
            //表示已经提交过，审核人员已经审核通过，后续不需要主动删除流程实例[审批通过的book，页面不提供删除功能，理论上不会出现这种情况]
        }else if (MyConstants.CHECK_NOT_PASSED.equals(book.getCheckStatus())){
            //表示已经提交过，审核人员已经审核,但未通过，后续不需要主动删除流程实例
        }else {
            // 其他情况，不应该出现（这里不会出现“待完善”）
            throw new RuntimeException("错误的checkStatus值："+book.getCheckStatus());
        }
//        if (!StringUtils.hasText(book.getProcessInstanceId())){
//            //没有实例id：表示没有提交过。即：用户自行撤销，没有流程实例相关操作需要处理
//        }else {
//            // 当不通过时，流程引擎会自动删除流程实例，不需要手动操作；
//            if (!book.getCheckStatus().equals(MyConstants.CHECK_NOT_PASSED)){
//                BpmProcessInstanceCancelReqVO cancelReqVO = new BpmProcessInstanceCancelReqVO();
//                cancelReqVO.setId(book.getProcessInstanceId());
//                cancelReqVO.setReason("主动删除");
//                flowInstanceService.cancelProcessInstanceByStartUserForce(SecurityUtils.getLoginUser().getUserId(),cancelReqVO);
//            }
//        }
    }
}
